//
// SoapTrace.Net - A soap message monitoring utility. 
//
// Copyright (C) 2005 Matthew Ward
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Matthew Ward (mrward@users.sourceforge.net)


using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Xml;

namespace SoapTrace.Core.Formatters
{
	/// <summary>
	/// Creates formatted xml from an xml string.
	/// </summary>
	public class XmlFormatter
	{
		/// <summary>
		/// The markup delimiter colour (uses IE defaults).
		/// </summary>
		private Color m_MarkupDelimiterColour = Color.Blue;
		
		/// <summary>
		/// The element name colour (Uses IE defaults).
		/// </summary>
		private Color m_ElementColour = Color.FromArgb( 153, 0, 0 );
		
		/// <summary>
		/// The attribute value's colour.
		/// </summary>
		private Color m_AttributeValueColour = Color.Black;
		
		/// <summary>
		/// The attribute colour.
		/// </summary>
		private Color m_AttributeColour = Color.FromArgb( 153, 0, 0 );
		
		/// <summary>
		/// The element's text colour.
		/// </summary>
		private Color m_ElementTextColour = Color.Black;
		
		/// <summary>
		/// The comment colour (Uses IE default).
		/// </summary>
		private Color m_CommentColour = Color.FromArgb( 136, 136, 136 );
		
		/// <summary>
		/// The CData text colour.
		/// </summary>
		private Color m_CDataColour = Color.Black;
		
		/// <summary>
		/// Creates a new instance of the <see cref="XmlFormatter"/>
		/// class.
		/// </summary>
		public XmlFormatter( )
		{
		}
		
		/// <summary>
		/// Gets or sets the text colour to use for markup delimiters.
		/// </summary>
		public Color MarkupDelimiterColour
		{
			get
			{
				return m_MarkupDelimiterColour;
			}
			
			set
			{
				m_MarkupDelimiterColour = value;
			}
		}
	
		/// <summary>
		/// Gets or sets the text colour to use for elements. 
		/// </summary>
		public Color ElementColour
		{
			get
			{
				return m_ElementColour;
			}
			
			set
			{
				m_ElementColour = value;
			}
		}	
		
		/// <summary>
		/// Gets or sets the text colour to use for attribute values. 
		/// </summary>
		public Color AttributeValueColour
		{
			get
			{
				return m_AttributeValueColour;
			}
			
			set
			{
				m_AttributeValueColour = value;
			}
		}		
		
		/// <summary>
		/// Gets or sets the text colour to use for an element's text. 
		/// </summary>
		public Color ElementTextColour
		{
			get
			{
				return m_ElementTextColour;
			}
			
			set
			{
				m_ElementTextColour = value;
			}
		}		
		
		/// <summary>
		/// Gets or sets the text colour to use for comment text. 
		/// </summary>
		public Color CommentColour
		{
			get
			{
				return m_CommentColour;
			}
			
			set
			{
				m_CommentColour = value;
			}
		}		
		
		/// <summary>
		/// Gets or sets the text colour to use for CData text. 
		/// </summary>
		public Color CDataColour
		{
			get
			{
				return m_CDataColour;
			}
			
			set
			{
				m_CDataColour = value;
			}
		}			
		
		/// <summary>
		/// Gets or sets the text colour to use for attribute names. 
		/// </summary>
		public Color AttributeColour
		{
			get
			{
				return m_AttributeColour;
			}
			
			set
			{
				m_AttributeColour = value;
			}
		}		
		
		/// <summary>
		/// Creates a <see cref="FormattedText"/> object from the
		/// specified xml <paramref name="text"/>.
		/// </summary>
		/// <param name="text">Xml text.</param>
		/// <returns>Formatted xml.</returns>
		public FormattedText Format( string text )
		{
			FormattedText FormattedText = new FormattedText( m_MarkupDelimiterColour );
			
			try
			{	
				FormattedText.Append( text );
							
				StringReader StringReader = new StringReader( text );
				XmlTextReader XmlTextReader = new XmlTextReader( StringReader );

				while( XmlTextReader.Read() )
				{					



					switch( XmlTextReader.NodeType )
					{
				    	case XmlNodeType.Element:
						{
							int CharIndex = FormattedText.GetCharIndexFromLine( XmlTextReader.LineNumber - 1, XmlTextReader.LinePosition - 1 );
							FormattedText.SetColour( m_ElementColour, CharIndex, XmlTextReader.Name.Length );
						
							if( XmlTextReader.HasAttributes )
							{
								SetAttributesTextColour( FormattedText, XmlTextReader );
							}
						}
						break;
						
						case XmlNodeType.Text:
						{
							int CharIndex = FormattedText.GetCharIndexFromLine( XmlTextReader.LineNumber - 1, XmlTextReader.LinePosition - 1 );
							FormattedText.SetColour( m_ElementTextColour, CharIndex, XmlTextReader.Value.Length );
						}
						break;
						
						case XmlNodeType.Comment:
						{
							int CharIndex = FormattedText.GetCharIndexFromLine( XmlTextReader.LineNumber - 1, XmlTextReader.LinePosition - 1 );
							FormattedText.SetColour( m_CommentColour, CharIndex, XmlTextReader.Value.Length );
						}
						break;
						
						case XmlNodeType.CDATA:
						{
							int CharIndex = FormattedText.GetCharIndexFromLine( XmlTextReader.LineNumber - 1, XmlTextReader.LinePosition - 1 );
							FormattedText.SetColour( m_CDataColour, CharIndex, XmlTextReader.Value.Length );
						}
						break;					
					
						case XmlNodeType.EndElement:
						{
							int CharIndex = FormattedText.GetCharIndexFromLine( XmlTextReader.LineNumber - 1, XmlTextReader.LinePosition - 1 );
							FormattedText.SetColour( m_ElementColour, CharIndex, XmlTextReader.Name.Length );
						}
						break;
					}
				}
				
				XmlTextReader.Close( );
			}
			catch( XmlException Ex )
			{
				// Ignore.
				
				Debug.WriteLine( Ex.Message );
			}
			
			return FormattedText;
		}

		
		/// <summary>
		/// Sets the attribute value colour for the current XmlTextReader node.
		/// </summary>
		/// <param name="formattedText">The text to format.</param>
		/// <param name="xmlTextReader">The XmlTextReader at the current node 
		/// containing attributes.</param>
		private void SetAttributesTextColour( FormattedText formattedText, XmlTextReader xmlTextReader )
		{
			for( int i = 0; i < xmlTextReader.AttributeCount; ++i )
			{
				xmlTextReader.MoveToAttribute( i );
				
				int CharIndex = formattedText.GetCharIndexFromLine( xmlTextReader.LineNumber - 1, xmlTextReader.LinePosition - 1 );
				formattedText.SetColour( m_AttributeColour, CharIndex, xmlTextReader.Name.Length );
				
				xmlTextReader.ReadAttributeValue( );
				
				CharIndex = formattedText.GetCharIndexFromLine( xmlTextReader.LineNumber - 1, xmlTextReader.LinePosition - 1 );
				formattedText.SetColour( m_AttributeValueColour, CharIndex, xmlTextReader.Value.Length );
			}
		}
	}
}
